package JET.physics.steering;

import JET.Centers.ControlCenter;
import JET.geom.Vec2d;
import JET.geom.Vec3d;
import JET.physics.engine.JETContext;
import JET.physics.Mass;
import JET.physics.bodies.SpringBody;
import JET.physics.interfaces.SceneTreeNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.w3c.dom.*;

/**
 *
 * @author 057Ry
 */
public class Torque implements SceneTreeNode {

    private Mass mass;                  // Mass specifies location
    private double value;               // Strength of torque
    private ArrayList<Mass> massList;   // List of masses affected by this torque
    private boolean active;
    private String name;

    private int id;
    private static int idCounter=0;

    private static Vec2d r = new Vec2d();
    private static Vec2d f = new Vec2d();

    public Torque(Mass mass, double value)
    {
        this.mass = mass;
        this.value = value;
        this.active = true;

        this.id = idCounter++;
        name = "Torque_"+id;
        
        massList = new ArrayList<Mass>();
    }

    public Torque(Element torqueXMLElement, JETContext engine)
    {
        id = idCounter++;
        massList = new ArrayList<Mass>();
        name = "Torque_"+id;
        
        NodeList children = torqueXMLElement.getChildNodes();
        for(int i=0; i<children.getLength(); i++)
        {
            Node node = children.item(i);

            if (node instanceof Element)
            {
                Element elem = (Element)node;

                if (elem.getTagName().equals("name"))
                {
                    Text textNode = (Text) elem.getFirstChild();
                    this.name = textNode.getData().trim();
                }
                else if (elem.getTagName().equals("active"))
                {
                    Text textNode = (Text) elem.getFirstChild();
                    String text = textNode.getData().trim();
                    this.active = Boolean.parseBoolean(text);
                }
                else if (elem.getTagName().equals("strength"))
                {
                    Text textNode = (Text) elem.getFirstChild();
                    String text = textNode.getData().trim();
                    this.value = Double.parseDouble(text);
                }
                else if (elem.getTagName().equals("center"))
                {
                    SpringBody sb=null;

                    NodeList centerNodeChildren = elem.getChildNodes();
                    for(int cnc = 0; cnc<centerNodeChildren.getLength(); cnc++)
                    {
                        Element cnChildEl = (Element)centerNodeChildren.item(cnc);
                        if (cnChildEl.getTagName().equals("type"))
                        {
                            Text textNode = (Text) cnChildEl.getFirstChild();
                            String text = textNode.getData().trim();
                            // TODO : readed type should be saved - when multiple types implemented
                        }
                        else if (cnChildEl.getTagName().equals("springBody_id"))
                        {
                            Text textNode = (Text) cnChildEl.getFirstChild();
                            String text = textNode.getData().trim();
                            int sbIndex = Integer.parseInt(text);
                            sb = engine.getSpringBodyList().get(sbIndex);
                        }
                        else if (cnChildEl.getTagName().equals("mass_id"))
                        {
                            Text textNode = (Text) cnChildEl.getFirstChild();
                            String text = textNode.getData().trim();
                            int massIndex = Integer.parseInt(text);
                            this.mass = sb.getMassList().get(massIndex);
                        }
                    }
                }
                else if (elem.getTagName().equals("mass"))
                {

                    SpringBody sb=null;
                    Mass m=null;

                    NodeList massNodeChildren = elem.getChildNodes();
                    for(int mnc = 0; mnc<massNodeChildren.getLength(); mnc++)
                    {
                        Element mnChildEl = (Element)massNodeChildren.item(mnc);
                        if (mnChildEl.getTagName().equals("springBody_index"))
                        {
                            Text textNode = (Text) mnChildEl.getFirstChild();
                            String text = textNode.getData().trim();
                            int sbIndex = Integer.parseInt(text);
                            sb = engine.getSpringBodyList().get(sbIndex);
                        }
                        else if (mnChildEl.getTagName().equals("index"))
                        {
                            Text textNode = (Text) mnChildEl.getFirstChild();
                            String text = textNode.getData().trim();
                            int massIndex = Integer.parseInt(text);
                            m = sb.getMassList().get(massIndex);
                        }
                    }

                    this.massList.add(m);
                }
            }
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public Mass getMass() {
        return mass;
    }

    public double getValue()
    {
        return value;
    }

    public void setMass(Mass mass) {
        this.mass = mass;
    }

    public void setValue(double value)
    {
        this.value = value;
    }

    public ArrayList<Mass> getMassList() {
        return massList;
    }

    public Collection<Mass> getMassListSync() {
        return Collections.synchronizedList(massList);
    }

    public synchronized void doYourJob()
    {
        if (mass==null) return;
        
        for(Mass m : massList)
            if (!mass.equals(m))
            {
                r.become(mass.getPosition());
                r.sub(m.getPosition());

                f.become(r);
                f.makePerp();
                f.normalize();

                f.scale( value/r.getMagnitude() );
                m.getForce().add(f);
            }
    }
    public int getOrientation()
    {
        if (getValue()!=0)
            return (int)(getValue()/Math.abs(getValue()));
        else
            return 0;
    }

    public String toString()
    {
        return name+" ["+getValue()+"]";
    }

    public Element getXMLRepresentation(Document doc, JETContext engine)
    {
//        Element rootElement = doc.createElement("torque");
//
//        // Name
//        Element nameEl = doc.createElement("name");
//        nameEl.appendChild( doc.createTextNode(""+this.name));
//
//        // Is torque active?
//        Element activeEl = doc.createElement("active");
//        activeEl.appendChild( doc.createTextNode(""+this.active));
//
//        // Strength of torque
//        Element strengthEl = doc.createElement("strength");
//        strengthEl.appendChild( doc.createTextNode(""+this.value) );
//
//        // Center location
//        Element centerEl = doc.createElement("center");
//
//        // TODO: add more types (like spring body geom center ...)
//        Element centerTypeEl = doc.createElement("type");
//        centerTypeEl.appendChild( doc.createTextNode("mass") );
//
//        Element centerItemSBIndexEl = doc.createElement("springBody_id");
//        if (mass!=null)
//        {
//            SpringBody sb = engine.getSpringBodyThatOwnMass(mass);
//            int sbIndex = engine.getSpringBodyList().indexOf(sb);
//            centerItemSBIndexEl.appendChild( doc.createTextNode(""+sbIndex) );
//
//            Element centerItemIndexEl = doc.createElement("mass_id");
//            int centerMassIndex = sb.getMassList().indexOf(this.mass);
//            centerItemIndexEl.appendChild( doc.createTextNode(""+centerMassIndex) );
//
//            centerEl.appendChild( centerTypeEl );
//            centerEl.appendChild( centerItemSBIndexEl );
//            centerEl.appendChild( centerItemIndexEl );
//        }
//
//        rootElement.appendChild(nameEl);
//        rootElement.appendChild(activeEl);
//        rootElement.appendChild(strengthEl);
//        rootElement.appendChild(centerEl);
//
//        // Masses that torque affects
//        for(Mass m : massList)
//        {
//            Element massSBIndexEl = doc.createElement("springBody_index");
//            SpringBody sb = engine.getSpringBodyThatOwnMass(m);
//            int sbIndex = engine.getSpringBodyList().indexOf(sb);
//            massSBIndexEl.appendChild( doc.createTextNode(""+sbIndex));
//
//            Element massIndexEl = doc.createElement("index");
//            massIndexEl.appendChild( doc.createTextNode(""+sb.getMassList().indexOf(m)));
//
//            Element massEl = doc.createElement("mass");
//            massEl.appendChild(massSBIndexEl);
//            massEl.appendChild(massIndexEl);
//
//            rootElement.appendChild(massEl);
//        }
//
//        return rootElement;
        return null;
    }

    ////////////////////////////////////////////////////////////////////////////
    //                S C E N E   T R E E   I N T E R F A C E                 //
    ////////////////////////////////////////////////////////////////////////////

    public NameTreeNode nameTreeNode = new NameTreeNode();
    public StrengthTreeNode strengthTreeNode = new StrengthTreeNode();
    public MassesTreeNode massesTreeNode = new MassesTreeNode();
    public ActiveTreeNode activeTreeNode = new ActiveTreeNode();

    public SceneTreeNode getChild(int index) {
        switch (index) {
            case 0 : return nameTreeNode;
            case 1 : return strengthTreeNode;
            case 2 : return activeTreeNode;
            case 3 : return mass;
            case 4 : return massesTreeNode;
            
        }

        return null;
    }

    public int getChildCount() {
        return 5;
    }

    public boolean isLeaf() {
        return false;
    }

    public int getIndexOfChild(Object child) {
        if      (child instanceof NameTreeNode) return 0;
        else if (child instanceof StrengthTreeNode) return 1;
        else if (child instanceof ActiveTreeNode) return 2;
        else if (child instanceof Mass && child.equals(mass)) return 3;
        else if (child instanceof MassesTreeNode) return 4;
        else
            return -1;
    }

    public void resetNodeValue(Object o) {}

    public class NameTreeNode implements SceneTreeNode {
        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            name = o.toString();
        }

        public String toString() {
            return "name: '"+name+"'";
        }
    }

    public class StrengthTreeNode implements SceneTreeNode {

        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            try {
                value = Double.parseDouble(o.toString());
            } catch(NumberFormatException nfe) {}
        }

        public String toString() {
            return "strength: '"+value+"'";
        }
    }


    public class ActiveTreeNode implements SceneTreeNode {
        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            active = Boolean.parseBoolean(o.toString());
            ControlCenter.getInstance().GUIC.repaintAllSceneTabs();
        }

        public String toString() {
            return "active: "+active;
        }
    }

    public class MassesTreeNode implements SceneTreeNode {

        public SceneTreeNode getChild(int index) {
            return massList.get(index);
        }

        public int getChildCount() {
            return massList.size();
        }

        public boolean isLeaf() {
            return massList.size() == 0;
        }

        public int getIndexOfChild(Object child) {
            return massList.indexOf(child);
        }

        public void resetNodeValue(Object o) {}

        public String toString() {
            return "masses";
        }
    }
}